package it.mozzicato.apkwizard.model;

import it.mozzicato.apkwizard.*;
import it.mozzicato.apkwizard.dto.*;
import it.mozzicato.apkwizard.utils.*;

import java.io.*;
import java.util.*;

import javax.swing.JOptionPane;
import javax.xml.parsers.*;

import org.w3c.dom.*;

// Creato il 15/mar/2011

/**
 * @author Roberto Mozzicato
 * 
 */
public class ValuesTableModel extends AbstractResourceTableModel<ValueResource> {
	public static enum columns implements DescriptionEnum {
		NAME("Name"), VALUE("Value");

		private String desc;

		columns(String desc) {
			this.desc = desc;
		}

		@Override
		public String getDescription() {
			return desc;
		}
	}

	private String lastKey;

	public ValuesTableModel(String projectName) {
		super(projectName);
	}

	@Override
	public int getColumnCount() {
		return columns.values().length;
	}

	@Override
	public String getColumnName(int columnIndex) {
		return columns.values()[columnIndex].getDescription();
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return columnIndex == columns.VALUE.ordinal();
	}

	@Override
	protected Object getField(ValueResource entry, int columnIndex) {
		switch (columnIndex) {
		case 0:
			return entry.getName();

		case 1:
			return entry.getValue();
		}

		return null;
	}

	@Override
	protected void setField(ValueResource entry, int columnIndex, Object value) {
		if (columnIndex == columns.VALUE.ordinal()) {
			entry.setValue((String) value);
			
			try {
				updateValue(entry);
			}
			catch (IOException e) {
				JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	public void loadValues(final PublicResource publicResource, final SortableLocale language) {
		if (publicResource == null || language.getDirectory() == null)
			return;
		
		publicResource.clearValues();
		
		activeType = publicResource.getType();
		String key = getResourcekey(activeType, publicResource.getName(), language);
		if (key == null || key.equals(lastKey))
			return;

		lastKey = key;
		activeList = new ArrayList<ValueResource>();
		final File resourceXml = PathUtils.getFileInProjects(projectName, ApkWizard.SUBPATH_DECOMP, "res", language.getDirectory().getName(), activeType.getFileName());
		final File originalResourceXml = PathUtils.getFileInProjects(projectName, ApkWizard.SUBPATH_BACKUP, "res", language.getDirectory().getName(), activeType.getFileName());
		final File rollbackFile = new File(originalResourceXml.getPath() + ApkWizard.ROLLBACK_SUFFIX);
		final File rollbackDir = new File(originalResourceXml.getParentFile().getPath() + ApkWizard.ROLLBACK_SUFFIX);

		final boolean originalExists = originalResourceXml.exists();
		// Create a fast-access map to lookup the resources when will parse the original values from the backup file 
		final HashMap<String, ValueResource> fastMap = new HashMap<String, ValueResource>();
		
		XmlResParser parser = new XmlResParser() {
			@Override
			protected boolean parseElement(Element element, File parsedFile) {
				String name = element.getAttribute("name");
				
				if (activeType.getMaxCount() == 1 && publicResource.getName().equals(name)) {
					ValueResource entry;
					if(parsedFile == resourceXml || (entry = fastMap.get(name)) == null) {
						entry = new ValueResource(publicResource, language, element.getTextContent());
						
						if(parsedFile == resourceXml) {
							// If we are parsing the current file, set the value as the content of this element.
							entry.setValue(element.getTextContent());
							// Even if there isn't a backup copy of the file we assume that the original
							// value is the same as the current value.
							if(!originalExists && !rollbackFile.exists() && !rollbackDir.exists())
								entry.setOriginalValue(entry.getValue());
							// Load the map only if the backup file exists, else save some resource...
							else
								fastMap.put(entry.getName(), entry);
						}
						activeList.add(entry);
						publicResource.addValue(entry);
					}

					// If we are parsing the backup copy the just set the original value
					if(parsedFile == originalResourceXml)
						entry.setOriginalValue(element.getTextContent());
			
					return false;
				}
				return true;
			}
		};
		parser.parseFile(resourceXml);
		
		if(originalExists)
			parser.parseFile(originalResourceXml);

		fireTableDataChanged();
	}
	
	public void addValue(ValueResource res) throws IOException {
		if(activeList == null)
			return;
		
		editValue(res, ResourceOperation.ADD);
		res.getPublicResource().addValue(res);
		activeList.add(res);
		fireTableRowsInserted(getRowCount()-1, getRowCount()-1);
	}
	
	public void removeValue(ValueResource res, SortableLocale[] languageDirs) throws IOException {
		if(activeList == null)
			return;
		
		editValue(res, ResourceOperation.REMOVE);
		res.getPublicResource().removeValue(res);
		
		int pos = activeList.indexOf(res);
		if(pos >= 0) {
			activeList.remove(pos);
			fireTableRowsDeleted(pos, pos);
		}
	}

	public void updateValue(ValueResource res) throws IOException {
		if(activeList == null)
			return;

		editValue(res, ResourceOperation.UPDATE);
	}

	private void editValue(ValueResource res, ResourceOperation operation) throws IOException {
		File xmlFile = PathUtils.getFileInProjects(projectName, ApkWizard.SUBPATH_DECOMP, "res", res.getLanguage().getDirectory().getName(), res.getType().getFileName());
		try {
			Document doc; 
			if(xmlFile.exists()) {
				DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
				doc = db.parse(xmlFile);
			}
			else
				doc = createEmptyResourceFile(xmlFile);
			
			Node root = doc.getFirstChild();

			if(operation == ResourceOperation.ADD) {
				Element child = doc.createElement(res.getType().getDescription());
				child.setAttribute("name", res.getName());
				child.setTextContent(res.getValue());
				root.appendChild(child);
			}
			else {
				Node child = root.getFirstChild();
				while (child != null) {
					if (child instanceof Element) {
						String name = ((Element) child).getAttribute("name");
						if(res.getName().equals(name)) {
							if(operation == ResourceOperation.UPDATE) {
								child.setTextContent(res.getValue());
							}
							else if(operation == ResourceOperation.REMOVE) {
								root.removeChild(child);
							}
							break;
						}
					}
					child = child.getNextSibling();
				}
			}
			
			backupAndSave(doc, xmlFile);
		}
		catch (Exception e) {
			if(operation == ResourceOperation.ADD)
				res.getPublicResource().removeValue(res);
			e.printStackTrace();
			throw new IOException("Unable to parse the xml file '" + xmlFile + "'");
		}
	}
}
